package main

import (
	"log"
	"sync"
	"time"
)

// sync.Cond (条件变量原语)
// 一个或一组 goroutine 等待条件成立，当条件成立时，这些 goroutine 将会被唤醒进行后续的工作

type signal struct{}

var ready bool

func worker(n int) {
	log.Printf("worker %d is working...", n)
	time.Sleep(1 * time.Second)
	log.Printf("worker %d works done!", n)
}

func spawnGroup(f func(n int), num int, groupSignal *sync.Cond) <-chan signal {
	c := make(chan signal)

	var wg sync.WaitGroup

	for i := 0; i < num; i++ {
		wg.Add(1)

		go func(n int) {

			groupSignal.L.Lock()

			if !ready {
				groupSignal.Wait()
			}

			groupSignal.L.Unlock()

			f(n)

			wg.Done()

		}(i + 1)
	}

	go func() {
		wg.Wait()

		c <- signal(struct{}{})
	}()

	return c
}

func main() {

	// sync.Cond 实例的初始化，需要一个满足实现了 sync.Locker 接口的类型实例，通常使用 sync.Mutex。
	groupSignal := sync.NewCond(&sync.Mutex{})

	c := spawnGroup(worker, 5, groupSignal)

	time.Sleep(3 * time.Second) // 模拟 ready 前的准备工作

	groupSignal.L.Lock()
	ready = true
	groupSignal.Broadcast()
	groupSignal.L.Unlock()

	<-c
}
